/// <reference path="Xpcom.js">
///--------------------------------------------------------------------
/// <file name="Color.js">
///   Encapsulates the components of a color.
/// </file>
///--------------------------------------------------------------------

NAMFox.ns(function() { with (NAMFox) {

this.ColorType = {
    Rgb: 0,
    Hsl: 1
};

this.Color = function(colorType, one, two, three) {
    one = parseInt(one);
    two = parseInt(two);
    three = parseInt(three);
    
    switch (colorType) {
        case ColorType.Rgb:
            this.red = Math.round(one);
            this.green = Math.round(two);
            this.blue = Math.round(three);
            this._toHsl();
            break;
        case ColorType.Hsl:
            this.hue = Math.round(one);
            this.saturation = Math.round(two);
            this.lightness = Math.round(three);
            this._toRgb();
            break;
        default:
            throw new Error("Unrecognized ColorType value.");
    }
}

Color.prototype = {
    red: null,
    green: null,
    blue: null,
    hue: null,
    saturation: null,
    lightness: null,
    
    _toHsl: function() {
        /// <summary>
        /// Sets the HSL components of this color, when the given values are RGB.
        /// </summary>
        
        var red = this.red / 255;
        var green = this.green / 255;
        var blue = this.blue / 255;
        
        var min = { component: "", value: 0 };
        var max = { component: "", value: 0 };
        
        if (red > green) {
            if (red > blue) {
                max.component = "r";
                max.value = red;
                
                if (green > blue) {
                    min.component = "b";
                    min.value = blue;
                }
                else {
                    min.component = "g";
                    min.value = green;
                }
            }
            else {
                max.component = "b";
                max.value = blue;
                
                min.component = "g";
                min.value = green;
            }
        }
        else {
            if (green > blue) {
                max.component = "g";
                max.value = green;
                
                if (red > blue) {
                    min.component = "b";
                    min.value = blue;
                }
                else {
                    min.component = "r";
                    min.value = red;
                }
            }
            else {
                max.component = "b";
                max.value = blue;
                
                min.component = "r";
                min.value = red;
            }
        }
        
        var sum = max.value + min.value;
        var difference = max.value - min.value;
        
        var hue;
        var saturation;
        var lightness = sum / 2;
        
        if (min.value === max.value) {
            hue = saturation = 0;
        }
        else {
            if (lightness < 0.5) {
                saturation = difference / sum;
            }
            else {
                saturation = difference / (2 - max.value - min.value);
            }
            
            switch (max.component) {
                case "r":
                    hue = (green - blue) / difference;
                    break;
                case "g":
                    hue = 2 + (blue - red) / difference;
                    break;
                case "b":
                    hue = 4 + (red - green) / difference;
                    break;
            }
        }
        
        hue *= 60;
        
        if (hue < 0) {
            hue += 360;
        }
        
        // 17/24 = 255/360
        this.hue = hue * (17/24);
        this.saturation = saturation * 255;
        this.lightness = lightness * 255;
    },
    
    _toRgb: function() {
        /// <summary>
        /// Sets the RGB components of this color, when the given values are HSL.
        /// </summary>
        var hue = this.hue / 255;
        var saturation = this.saturation / 255;
        var lightness = this.lightness / 255;
        
        var temp1, temp2, colors;
        
        if (saturation === 0) {
            this.red = this.green = this.blue = this.lightness;
        }
        else {
            if (lightness < 0.5) {
                temp2 = lightness * (1 + saturation);
            }
            else {
                temp2 = lightness + saturation - lightness * saturation;
            }
            
            temp1 = 2 * lightness - temp2;
            
            colors = [hue + 1/3, hue, hue - 1/3];
            for (var i = 0; i < 3; ++i) {
                var color = colors[i];
                
                if (color < 0) {
                    color += 1;
                }
                else if (color > 1) {
                    color -= 1;
                }
                
                if (6 * color < 1) {
                    colors[i] = temp1 + (temp2 - temp1) * 6 * color;
                }
                else if (2 * color < 1) {
                    colors[i] = temp2;
                }
                else if (3 * color < 2) {
                    colors[i] = temp1 + (temp2 - temp1) * (2/3 - color) * 6;
                }
                else {
                    colors[i] = temp1;
                }
            }
            
            this.red = Math.round(colors[0] * 255)
            this.green = Math.round(colors[1] * 255);
            this.blue = Math.round(colors[2] * 255);
        }
    },
    
    toHex: function() {
        return "#" +
            $.padLeft(this.red.toString(16), 2, '0') +
            $.padLeft(this.green.toString(16), 2, '0') +
            $.padLeft(this.blue.toString(16), 2, '0');
    }
};

Color.fromHex = function(hex) {
    /// <summary>
    /// Creates a new color from a hex code.
    /// </summary>
    /// <param name="hex" type="String">
    /// The six-digit hex code of the color.
    /// </param>
    /// <returns type="Color" />
    
    if (hex.length === 7) {
        hex = hex.substr(1);
    }
    
    var red = parseInt(hex.substr(0, 2), 16);
    var green = parseInt(hex.substr(2, 2), 16);
    var blue = parseInt(hex.substr(4, 2), 16);
    
    return new Color(ColorType.Rgb, red, green, blue);
};

Color.fromHsl = function(hue, saturation, lightness) {
    /// <summary>
    /// Creates a new color from a given hue, saturation, and lightness.
    /// </summary>
    /// <param name="hue" type="Number" integer="true">
    /// The hue of the new color.
    /// </param>
    /// <param name="saturation" type="Number" integer="true">
    /// The saturation of the new color.
    /// </param>
    /// <param name="lightness" type="Number" integer="true">
    /// The lightness of the new color.
    /// </param>
    /// <returns type="Color" />
    
    return new Color(ColorType.Hsl, hue, saturation, lightness);
};

Color.fromRgb = function(red, green, blue) {
    /// <summary>
    /// Creates a new color from a given red, green, and blue value.
    /// </summary>
    /// <param name="red" type="Number" integer="true">
    /// The redness of the new color.
    /// </param>
    /// <param name="green" type="Number" integer="true">
    /// The greenness of the new color.
    /// </param>
    /// <param name="blue" type="Number" integer="true">
    /// The blueness of the new color.
    /// </param>
    /// <returns type="Color" />
    
    return new Color(ColorType.Rgb, red, green, blue);
};

Color.fromCss = function(color) {
    /// <summary>
    /// Creates a new color from a CSS specification for color e.g. rgb(123, 12, 1)
    /// </summary>
    /// <param name="color" type="String">
    /// The text from CSS e.g. rgb(13, 42, 91)
    /// </param>
    /// <returns type="Color" />

    var matches = color.match(/(\d{1,3}), (\d{1,3}), (\d{1,3})/);

    if (matches) {
        return Color.fromRgb(matches[1], matches[2], matches[3]);
    }

    throw new Error("Could not parse input color: " + color);
}

}});